package simpleserver;

import java.io.File;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.io.FileDeleteStrategy;
import org.apache.commons.io.FileUtils;

/**
 * @author Daniela
 * 
 *         agent can only handle with .txt files that contains one ip per row
 * 
 */
public class MobAgent {

	static boolean copyFullFolder = true;
	static String myRelMyFiles = "MyFiles";
	static String myRelNimmDiesMit = "NimmDiesMit";

	static String myDataRelpath = "MyFiles/";
	static String myNextGoals = myDataRelpath + "meine_naechsten_Ziele.txt";
	static String myTakeMeWithYou = myDataRelpath + "Mitgenommen.txt";

	static String relpathNew = "Files/";
	static String relpathNimmDiesMit = "NimmDiesMit/";
	static String nextGoals = relpathNew + "die_naechsten_Ziele.txt";
	static String takeMeWithYou = relpathNew + "Nimm_dies_mit.txt";

	static String pathClass = "bin/simpleserver/";
	static String myClass = pathClass + "MobAgent.class";

	File myNextGoalsF;
	File myTakeMeWithYouF;
	File nextGoalsF;
	File takeMeWithYouF;
	File me = new File(myClass);

	private String serverHostname = null;
	private int serverPort = 0;
	private Socket sock = null;
	private OutputStream sockOutput = null;

	public MobAgent() {

		if (fileExist(myNextGoals)) {
			this.myNextGoalsF = new File(myNextGoals);
		} else {
			try {
				FileUtils.write(FileUtils.getFile(myNextGoals), "");
				this.myNextGoalsF = new File(myNextGoals);
			} catch (IOException e) {
				System.out.println(myNextGoals
						+ " konnte nicht erstellt werden");
				e.printStackTrace();
			}
		}

		if (fileExist(myTakeMeWithYou)) {
			this.myTakeMeWithYouF = new File(myTakeMeWithYou);
		} else {
			try {
				FileUtils.write(FileUtils.getFile(myTakeMeWithYou), "");
				this.myTakeMeWithYouF = new File(myTakeMeWithYou);
			} catch (IOException e) {
				System.out.println(myTakeMeWithYou
						+ " konnte nicht erstellt werden");
				e.printStackTrace();
			}
		}

		if (fileExist(nextGoals)) {
			this.nextGoalsF = new File(nextGoals);
		} else {
			try {
				FileUtils.write(FileUtils.getFile(nextGoals), "");
				this.nextGoalsF = new File(nextGoals);
			} catch (IOException e) {
				System.out.println(nextGoals + " konnte nicht erstellt werden");
				e.printStackTrace();
			}
		}

		if (fileExist(takeMeWithYou)) {
			this.takeMeWithYouF = new File(takeMeWithYou);
		} else {

			try {
				FileUtils.write(FileUtils.getFile(takeMeWithYou), "");
				this.takeMeWithYouF = new File(takeMeWithYou);
			} catch (IOException e) {
				System.out.println(takeMeWithYou
						+ " konnte nicht erstellt werden");
				e.printStackTrace();
			}
		}
	}

	public void getConnection(String serverHostname, int serverPort) {
		this.serverHostname = serverHostname;
		this.serverPort = serverPort;
		System.out.println("ip und port setzten" + "IP = "
				+ this.serverHostname + " PORT = " + this.serverPort);
	}

	public boolean testConnectionAndSendIfOk() {
		System.err.println("Opening connection to " + serverHostname + " port "
				+ serverPort);
		try {
			sock = new Socket(serverHostname, serverPort);
			sockOutput = sock.getOutputStream();
		} catch (IOException e) {
			System.out.println("Agent: keine Verbindung hergestellt");
			return false;
		}
		System.out.println("weiter in get connection");
		ObjectOutputStream oos;

		try {
			oos = new ObjectOutputStream(sockOutput);
			oos.writeObject(FileUtils.readFileToByteArray(me));
			oos.writeObject(FileUtils.readLines(myNextGoalsF));
			oos.writeObject(FileUtils.readLines(myTakeMeWithYouF));
			
			if (copyFullFolder == true) {
				List<Map<String, byte[]>> data = convertFolderToList(myDataRelpath, false);
				oos.writeObject(data);
			}

			oos.close();

			System.out.println("Files wurden versendet");

		} catch (IOException e1) {
			e1.printStackTrace();
			closeConnection();
			return false;
		}

		closeConnection();
		return true;
	}

	void closeConnection() {
		try {
			sock.close();
			System.err.println("Exiting.");
		} catch (IOException e) {
			System.err.println("Exception closing socket.");
			e.printStackTrace(System.err);
		}
	}

	/**
	 * @param filename
	 * @return true is file is already in expected folder and can be read else
	 *         false
	 */
	public static boolean fileExist(String filename) {
		File newF = new File(filename);
		return newF.canRead();
	}

	/**
	 * @param file
	 * @return true if File contains one Line of Text (ignores Whitespace)
	 */
	public static boolean isFileEmpty(File file) {
		try {
			List<String> l = FileUtils.readLines(file);
			for (String s : l) {
				String pure = s.trim();
				if (!pure.isEmpty()) {
					return false;
				}
			}
		} catch (IOException e) {
			System.out.println("File " + file + " ist leer");
		}
		return true;
	}

	/**
	 * reads lines of file "die_naechsten_Ziele.txt" and appends them to the
	 * lines of myNextGoalsF
	 * 
	 * @return true if action was prosper else false
	 */
	boolean saveNextGoals() {
		if (isFileEmpty(nextGoalsF)) {
			return true;
		}
		try {
			List<String> goals = FileUtils.readLines(nextGoalsF);
			List<String> cleangoals = new LinkedList<String>();
			List<String> mygoals = FileUtils.readLines(myNextGoalsF);

			List<String> tempmygoals = new LinkedList<String>(mygoals);
			for (String g : tempmygoals) {
				if (g.endsWith("#")) {
					tempmygoals.set(tempmygoals.indexOf(g), g.replace("#", ""));
				}
			}

			for (String goal : goals) {
				goal = goal.trim();
				if (!goal.isEmpty() && !tempmygoals.contains(goal)) {
					cleangoals.add(goal);
				}
			}

			mygoals.addAll(cleangoals);
			FileUtils.writeLines(myNextGoalsF, mygoals);
			return true;

		} catch (IOException e) {
			System.out.println(nextGoals + " konnte nicht ausgelesen werden");
			e.printStackTrace();
		}
		return false;
	}

	boolean readTakeThisWithYou() {
		if (isFileEmpty(takeMeWithYouF)) {
			return true;
		}
		try {
			List<String> takeme = FileUtils.readLines(takeMeWithYouF);
			List<String> mytakeme = FileUtils.readLines(myTakeMeWithYouF);
			mytakeme.addAll(takeme);
			FileUtils.writeLines(myTakeMeWithYouF, mytakeme);
			return true;

		} catch (IOException e) {
			System.out.println(takeMeWithYou
					+ " konnte nicht ausgelesen werden");
			e.printStackTrace();
		}
		return false;
	}

	public List<Map<String, byte[]>> convertFolderToList(String folderpath,
			boolean renamepathtomydata) {

		List<Map<String, byte[]>> resultList = new LinkedList<Map<String, byte[]>>();
		File rootfolder = new File(folderpath);
		if (rootfolder.exists()) {

			Iterator iter = FileUtils.iterateFiles(rootfolder, null, true);

			while (iter.hasNext()) {
				File current = (File) iter.next();
				String curpath = current.toString();

				if (renamepathtomydata == true) {

					curpath = curpath.replace(myRelNimmDiesMit, myRelMyFiles);
				}

				try {
					byte[] curbyte = FileUtils.readFileToByteArray(current);
					Map<String, byte[]> entrypair = new HashMap<String, byte[]>();
					entrypair.put(curpath, curbyte);
					resultList.add(entrypair);

				} catch (IOException e) {

					e.printStackTrace();
				}
			}

		} else {
			System.out.println("convertFolderToList: ordner " + folderpath
					+ " existierte nicht");
		}
		return resultList;
	}

	public boolean convertListToFolder(List<Map<String, byte[]>> inputlist) {

		for (Map<String, byte[]> entry : inputlist) {

			Set<String> set = entry.keySet();

			for (String s : set) {
				File currfile = new File(s);
				try {
					FileUtils.writeByteArrayToFile(currfile, entry.get(s));
				} catch (IOException e) {
					e.printStackTrace();
					System.out
							.println("convertListToFolder: Dateien konten nicht korrekt wieder hergestellt werden");
					return false;
				}
			}
		}
		return true;
	}
	
	public boolean saveFolderNimmDiesMit(){
		
		List<Map<String, byte[]>> lis =convertFolderToList(myRelNimmDiesMit,true);
		if(lis.isEmpty()){
		return false;
		}
		return convertListToFolder(lis);
		
	}

	boolean hasNextTarget() {
		try {
			List<String> l = FileUtils.readLines(myNextGoalsF);
			for (String zeile : l) {
				if (!zeile.endsWith("#") && !zeile.isEmpty()) {
					return true;
				}
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		return false;
	}

	/**
	 * @return String (ip:port) next station for agent
	 */
	String getNextTarget() {

		if (hasNextTarget()) {
			try {
				List<String> l = FileUtils.readLines(myNextGoalsF);
				for (String zeile : l) {
					if (!zeile.isEmpty() && !zeile.endsWith("#")) {
						return zeile;
					}
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		return null;
	}

	String getHomeTarget() {
		try {
			List<String> l = FileUtils.readLines(myNextGoalsF);
			for (String zeile : l) {
				if (!zeile.isEmpty() && zeile.endsWith("#")) {
					return zeile;
				}
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		return null;
	}

	String getHomeIP() {
		String home = getHomeTarget();
		if (home != null) {
			String[] arr = home.split(":");
			return arr[0];
		}
		System.out.println("getHomeIP nicht gefunden");
		return null;
	}

	int getHomePort() {
		String home = getHomeTarget();
		if (home != null) {
			String[] arr = home.split(":");
			int port = Integer.parseInt(arr[1]);
			return port;
		}
		System.out.println("getHomePort nicht gefunden");
		return 0;
	}

	/**
	 * appends to the last not jet marked ip:port entry in textfile a #
	 * 
	 * @return true if successful
	 */
	boolean markLastGoalAsDone() {
		try {
			List<String> l = FileUtils.readLines(myNextGoalsF);

			for (String zeile : l) {
				if (!zeile.endsWith("#") && !zeile.isEmpty()) {
					l.set(l.indexOf(zeile), zeile + "#");
					FileUtils.writeLines(myNextGoalsF, l);
					return true;
				}
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		return false;
	}

	boolean haveToGoHome() {
		if (fileExist(myNextGoals) == true && !isFileEmpty(myNextGoalsF)
				&& getNextTarget() == null) {
			return true;
		}
		return false;
	}

	String getNextIp() {
		String target = getNextTarget();
		if (target != null) {
			String[] arr = target.split(":");
			return arr[0];
		}
		System.out.println("getNextIp nicht gefunden");
		return null;
	}

	int getNextPort() {
		String target = getNextTarget();
		if (target != null) {
			String[] arr = target.split(":");
			int port = Integer.parseInt(arr[1]);
			return port;
		}
		System.out.println("getNextPort nicht gefunden");
		return 0;
	}

	public void agentWork() {
		System.out.println("agentwork:");

		// Wenn Agent auf dem ersten rechner gestratet wird ist seine liste mit
		// Zielen leer
		// dann soll als erstes die ip des startrechners auf der liste
		// eingefuegt werden
		if (isFileEmpty(myNextGoalsF)) {

			InetAddress thisIp;
			try {
				thisIp = InetAddress.getLocalHost();
				System.out.println("Agenten Start IP (Rueckkehradresse): "
						+ thisIp.getHostAddress());
				try {
					List<String> mygoals = FileUtils.readLines(myNextGoalsF);
					mygoals.add(thisIp.getHostAddress() + ":54000");
					FileUtils.writeLines(myNextGoalsF, mygoals);

				} catch (IOException e) {
					e.printStackTrace();
				}

			} catch (UnknownHostException e) {
				e.printStackTrace();
			}
		}

		saveNextGoals();
		markLastGoalAsDone();
		readTakeThisWithYou();
		if(copyFullFolder == true){
			saveFolderNimmDiesMit();
		}

		boolean propersend = false;

		while (propersend == false) {

			System.out.println("agentWork: sendeversuch schleife");

			String ip;
			int port;
			if (haveToGoHome() == true) {
				System.out.println("agentWork: haveToGoHome");

				InetAddress thisIp;
				try {
					thisIp = InetAddress.getLocalHost();
					if (thisIp.getHostAddress().equals(getHomeIP())) {
						presentResultsFromVoyage();
						System.out.println();
						return;
					}
				} catch (UnknownHostException e) {
					System.out.println("agentWork: UnknownHostException");
					e.printStackTrace();
				}

				ip = getHomeIP();
				port = getHomePort();
				getConnection(ip, port);

			} else {
				ip = getNextIp();
				port = getNextPort();
				getConnection(ip, port);
			}

			propersend = testConnectionAndSendIfOk();

			System.out.println("-----PROPERSEND= " + propersend);

			if (propersend == false) {
				System.out.println("propersend == false");
				markLastGoalAsDone();
			}

			if (propersend == true) {
				System.out.println("propersend == true");
				System.out.println("Uebertragen");
				FileDeleteStrategy.NORMAL.deleteQuietly(myTakeMeWithYouF);
				FileDeleteStrategy.NORMAL.deleteQuietly(myNextGoalsF);
			}
		}
	}

	void presentResultsFromVoyage() {

		try {
			List<String> res = FileUtils.readLines(takeMeWithYouF);

			for (String r : res) {
				System.out.println(r);
			}
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

	public void alive() {
		System.out.println("yes...alive");

	}

	public static void main(String[] args) throws IOException {

		MobAgent mob = new MobAgent();
//
//		File f = new File("NimmDiesMit/");
//		System.out.println(f.exists());
//
//		List<Map<String, byte[]>> li = mob.convertFolderToList(
//				relpathNimmDiesMit, true);
//		System.out.println(li);
//		mob.convertListToFolder(li);

	}

}
